﻿﻿using DotNetOpenAuth.AspNet;
using DotNetOpenAuth.AspNet.Clients;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using System.Xml.Linq;

namespace Tangerine.Web.Core.CustomAuthClients
{
    public class LinkedInCustomClient : OAuthClient
    {
        private static XDocument LoadXDocumentFromStream(Stream stream)
        {
            var settings = new XmlReaderSettings
            {
                MaxCharactersInDocument = 65536L
            };

            return XDocument.Load(XmlReader.Create(stream, settings));
        }

        /// Describes the OAuth service provider endpoints for LinkedIn.
        private static readonly ServiceProviderDescription LinkedInServiceDescription = new ServiceProviderDescription
        {
            AccessTokenEndpoint = new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/accessToken", HttpDeliveryMethods.PostRequest),
            RequestTokenEndpoint = new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/requestToken", HttpDeliveryMethods.PostRequest),
            UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://www.linkedin.com/uas/oauth/authorize", HttpDeliveryMethods.PostRequest),
            TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
            ProtocolVersion = ProtocolVersion.V10a
        };

        public LinkedInCustomClient(string consumerKey, string consumerSecret) :
            base("linkedIn", LinkedInServiceDescription, consumerKey, consumerSecret)
        { }

        /// Check if authentication succeeded after user is redirected back from the service provider.
        /// The response token returned from service provider authentication result.
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We don't care if the request fails.")]
        protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
        {
            // See here for Field Selectors API http://developer.linkedin.com/docs/DOC-1014
            const string profileRequestUrl = "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address,headline,industry,summary)";

            string accessToken = response.AccessToken;

            var profileEndpoint = new MessageReceivingEndpoint(profileRequestUrl, HttpDeliveryMethods.GetRequest);
            HttpWebRequest request = WebWorker.PrepareAuthorizedRequest(profileEndpoint, accessToken);

            try
            {
                using (WebResponse profileResponse = request.GetResponse())
                {
                    using (Stream responseStream = profileResponse.GetResponseStream())
                    {
                        XDocument document = LoadXDocumentFromStream(responseStream);
                        string userId = document.Root.Element("id").Value;
                        string firstName = document.Root.Element("first-name").Value;
                        string lastName = document.Root.Element("last-name").Value;
                        string userName = firstName + " " + lastName;
                        string email = document.Root.Element("email-address").Value;
                        var extraData = new Dictionary<string, string>
                                            {
                                                {"accesstoken", accessToken},
                                                {"name", userName},
                                                {"email", email}
                                            };
                        //IDictionary<string, string> extraData = new Dictionary<string, string>();
                        //extraData.Add("accesstoken", accessToken);
                        //extraData.Add("name", userName);
                        return new AuthenticationResult(isSuccessful: true,
                            provider: ProviderName,
                            providerUserId: userId,
                            userName: userName,
                            extraData: extraData);
                    }
                }
            }
            catch (Exception exception)
            {
                return new AuthenticationResult(exception);
            }
        }
    }



    /// <summary>
    /// The dictionary extensions.
    /// </summary>
    internal static class DictionaryExtensions
    {
        /// <summary>
        /// Adds the value from an XDocument with the specified element name if it's not empty.
        /// </summary>
        /// <param name="dictionary">
        /// The dictionary.
        /// </param>
        /// <param name="document">
        /// The document.
        /// </param>
        /// <param name="elementName">
        /// Name of the element.
        /// </param>
        internal static void AddDataIfNotEmpty(
            this NameValueCollection dictionary, XDocument document, string elementName)
        {
            var element = document.Root.Element(elementName);
            if (element != null)
            {
                dictionary.AddItemIfNotEmpty(elementName, element.Value);
            }
        }

        /// <summary>
        /// Adds a key/value pair to the specified dictionary if the value is not null or empty.
        /// </summary>
        /// <param name="dictionary">
        /// The dictionary.
        /// </param>
        /// <param name="key">
        /// The key.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        internal static void AddItemIfNotEmpty(this NameValueCollection dictionary, string key, string value)
        {
            if (key == null)
            {
                throw new ArgumentNullException("key");
            }

            if (!string.IsNullOrEmpty(value))
            {
                dictionary[key] = value;
            }
        }
    }
}